#pragma once

#include <iostream>
#include <string>
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <strings.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <functional>

#include <sys/wait.h>

#include "Log.hpp"
#include "InetAddr.hpp"

const static int backlog = 16;
enum
{
    SOCKET_ERROR = 1,
    BIND_ERROR,
    LISTEN_ERROR,
    USAGE_ERROR
};

using func_t = std::function<std::string(const std::string&)>;
class TcpServer;
//V2多线程时的封装
class ThreadData
{
public:
//注意InetAddr是自定义类型，拷贝构造函数是默认生成的浅拷贝，此处不影响
    ThreadData(int sockfd,InetAddr addr,TcpServer* tps):_sockfd(sockfd),_addr(addr)
    {
        self = tps;
    }

    ~ThreadData()
    {}

    int _sockfd;
    InetAddr _addr;
    TcpServer* self;
};

class TcpServer
{
public:
    TcpServer(uint16_t port,func_t func) : _port(port), _isrunning(false),_func(func)
    {
        _listen_sockfd = -1;
    }

    void InitServer()
    {
        // 1.创建套接字文件描述符
        _listen_sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
        if (_listen_sockfd < 0)
        {
            LOG(FATAL, "socket create error,errno:%d:%s\n", errno, strerror(errno));
            exit(SOCKET_ERROR);
        }
        LOG(INFO, "socker create success,sockfd:%d\n", _listen_sockfd);
        // 2.创建套接字
        struct sockaddr_in local;

        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;
        // 3.绑定
        int n = ::bind(_listen_sockfd, (struct sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            LOG(FATAL, "bind errror,errno:%d:%s\n", errno, strerror(errno));
            exit(BIND_ERROR);
        }
        LOG(INFO, "bind success\n");

        // 4.tcp面向链接，因此通信前要先建立链接，建立链接时，对于服务器，
        // tcp服务器一直处于监听状态
        n = ::listen(_listen_sockfd, backlog);
        if (n < 0)
        {
            LOG(FATAL, "listen errror,errno:%d:%s\n", errno, strerror(errno));
            exit(LISTEN_ERROR);
        }
        LOG(INFO, "listen success\n");
    }
    void Server(int sockfd,InetAddr addr)
    {
        LOG(DEBUG,"tcpserver get a new link,this is:%s:%d\n",addr.Ip().c_str(),addr.Port());

        std::string clinetmessage = "[" + addr.Ip()+":" +std::to_string(addr.Port())+"]#";
        while(1)
        {
            
            char buffer[1024];
            ssize_t n = recv(sockfd,buffer,sizeof(buffer)-1,0);
            if(n > 0)
            {
                buffer[n] = 0;
                std::cout << clinetmessage<<buffer<<std::endl;

                std::string echo_string = _func(buffer);


                ::send(sockfd,echo_string.c_str(),echo_string.size(),0);
            }
            else if(n == 0)
            {
                //读到了文件尾，在网络中意味着，客户端退出，关闭链接
                LOG(INFO,"%s quit\n",clinetmessage.c_str());
                break;
            }
            else
            {
                LOG(FATAL,"read error,errno:%d:%s\n", errno, strerror(errno));
                break;
            }

        }
        ::close(sockfd);
    }
    //成员函数，去掉this指针，但是又需要this指针
    static void* HandleTask(void * args)
    {
        pthread_detach(pthread_self());

        ThreadData* td = static_cast<ThreadData*>(args);
        //要让td执行Server（私有），得要有this指针
        
        td->self->Server(td->_sockfd,td->_addr);
        delete td;
        return nullptr;
    }
    void Loop()
    {
        _isrunning = true;
        while (_isrunning)
        {
            
            struct sockaddr_in peer; // 可能会给服务器发消息的远端套接字
            socklen_t len = sizeof(peer);
            int sockfd = ::accept(_listen_sockfd, (struct sockaddr *)&peer, &len);
            if(sockfd < 0)
            {
                LOG(WARNING, "accept errror,errno:%d:%s\n", errno, strerror(errno));
                continue;
            }
            LOG(INFO, "accept success\n");
            InetAddr one_peer(peer);

            //V2:多线程版本
            //要让线程看到的参数有sockfd、和addr
            //可以用类来封装
            pthread_t id;
            //传给HandleTask的是一个指针
            ThreadData* td = new ThreadData(sockfd,one_peer,this);
            pthread_create(&id,nullptr,HandleTask,td);//主线程要等待副线程，可以将线程分离
        }
        _isrunning = false;
    }

    ~TcpServer()
    {
        if(_listen_sockfd == -1)
        ;
        ::close(_listen_sockfd);
    }

private:
    int _listen_sockfd;
    uint16_t _port;

    func_t _func;
    bool _isrunning;
};